package backup.database;

import com.jfinal.config.Plugins;
import com.jfinal.plugin.activerecord.DbPro;
import com.jfinal.plugin.activerecord.Page;
import com.jfinal.plugin.activerecord.Record;
import com.mulanweb.api.config.AppConfig;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 数据库导出备份
 * Created by hcy on 2016/7/11.
 */
public class ExportDatabaseMain
{
    /**
     * 启动插件
     *
     * @param plugins 插件列表
     */
    private static void startPlugins(Plugins plugins)
    {
        plugins.getPluginList().forEach(p -> p.start());
    }

    /**
     * 停止插件
     *
     * @param plugins 插件
     */
    private static void stopPlugins(Plugins plugins)
    {
        plugins.getPluginList().forEach(p -> p.stop());
    }

    /**
     * 数据导出到该目录
     */
    private static final String EXPORT_DIR = "D:\\数据\\用户数据\\2.0\\程序备份\\";
    /**
     * 字段分隔符
     */
    private static final String SP         = ";";

    public static void main(String[] args) throws IOException
    {
        Plugins plugins = new Plugins();
        {
            AppConfig appConfig = new AppConfig();
            appConfig.configPlugin(plugins);
        }
        startPlugins(plugins);
        {
            exportDatabase(AppConfig.MULAN_XXL);
        }
        stopPlugins(plugins);
    }

    /**
     * 初始化导出文件
     *
     * @param configName  配置名称（数据库名称）
     * @param tableName   表名称
     * @param columnNames 字段列表
     *
     * @return 数据文件
     *
     * @throws IOException
     */
    private static File initFile(String configName, String tableName, String[] columnNames) throws IOException
    {
        File file = new File(EXPORT_DIR + new SimpleDateFormat("yyyyMMdd").format(new Date()) + "\\" + configName + "\\" + tableName + ".csv");
        if (file.exists() == false)
        {
            if (!file.getParentFile().exists())
            {
                file.getParentFile().mkdirs();
            }
            StringBuilder lineHeader = new StringBuilder();
            for (int j = 0; j < columnNames.length; j++)
            {
                if (j > 0)
                {
                    lineHeader.append(SP);
                }
                lineHeader.append(columnNames[j]);
            }
            lineHeader.append("\n");
            file.createNewFile();
            FileUtils.write(file, lineHeader.toString(), true);
        }
        return file;
    }

    /**
     * 导出数据库
     *
     * @param configName 配置名称（数据库名称）
     */
    private static void exportDatabase(String configName) throws IOException
    {
        DbPro        dbPro      = DbPro.use(configName);
        List<String> tableNames = getTables(DbPro.use(configName));
        for (int i = 0; i < tableNames.size(); i++)
        {
            String tableName = tableNames.get(i);
            System.out.println(tableName);
            exportTable(dbPro, tableName, initFile(configName, tableName, getColumnNames(dbPro, tableName)));
        }
    }

    /**
     * 获取数据库中的所有表
     *
     * @param dbPro 数据库操作实例
     *
     * @return 所有表名称
     */
    private static List<String> getTables(DbPro dbPro)
    {
        List<Record> tables = dbPro.find("SELECT [id], [name] FROM [sysobjects] WHERE [type] = 'u' ORDER BY [name]");

        List<String> tableNames = new ArrayList<>(tables.size());

        tables.forEach(r -> tableNames.add(r.get("name")));

        return tableNames;
    }

    /**
     * 导出表数据
     *
     * @param dbPro     数据库操作实例
     * @param tableName 表名称
     * @param file      导出到文件
     */
    private static void exportTable(DbPro dbPro, String tableName, File file)
    {
        String[] columnNames = getColumnNames(dbPro, tableName);
        paginationExportTable(dbPro, tableName, 1, 2000, columnNames, file);
    }

    /**
     * 分页导出表数据
     *
     * @param dbPro       数据库操作实例
     * @param tableName   表名称
     * @param pageNumber  页码
     * @param pageSize    每页查询记录条数
     * @param columnNames 字段列表
     * @param file        数据文件
     */
    private static void paginationExportTable(DbPro dbPro, String tableName, int pageNumber, int pageSize, String[] columnNames, File file)
    {
        Page<Record> page = dbPro.paginate(pageNumber, pageSize, "SELECT *", "FROM [" + tableName + "]");
        if (page.getList().size() > 0)
        {
            appendToFile(page.getList(), tableName, columnNames, file);
            if (page.getTotalPage() > pageNumber)
            {
                paginationExportTable(dbPro, tableName, 1 + pageNumber, pageSize, columnNames, file);
            }
        }
    }

    /**
     * 将数据追加到文件中
     *
     * @param records     查询到的记录
     * @param tableName   表名称
     * @param columnNames 字段列表
     * @param file        数据文件
     */
    private static void appendToFile(List<Record> records, String tableName, String[] columnNames, File file)
    {
        StringBuilder content = new StringBuilder();
        records.forEach(r -> content.append(margeToLine(r, columnNames)).append("\n"));

        try
        {
            FileUtils.write(file, content.toString(), true);
        } catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 将一个查询记录合并成一行
     *
     * @param record      查询记录
     * @param columnNames 字段列表
     *
     * @return 合并字段的字符串
     */
    private static String margeToLine(Record record, String[] columnNames)
    {
        StringBuilder line = new StringBuilder();
        for (int i = 0; i < columnNames.length; i++)
        {
            if (i > 0)
            {
                line.append(SP);
            }
            {
                Object value = record.get(columnNames[i]);
                if (value instanceof String)
                {
                    line.append("\"");
                    line.append(value.toString().replace("\"", "\\").replaceAll("\n", "\\n").replaceAll("\r", "\\n"));
                    line.append("\"");
                } else if (value instanceof Date)
                {
                    line.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) value));
                } else
                {
                    if (null != value)
                    {
                        line.append(value.toString());
                    }
                }
            }
        }

        return line.toString();
    }

    /**
     * 获取表字段列表
     *
     * @param dbPro     数据库操作对象
     * @param tableName 表名称
     *
     * @return 字段列表
     */
    private static String[] getColumnNames(DbPro dbPro, String tableName)
    {
        List<Record> records = dbPro.find("SELECT name FROM SysColumns WHERE id=Object_Id(?) ORDER BY colid", tableName);

        String[] columnNames = new String[records.size()];
        for (int i = 0; i < records.size(); i++)
        {
            columnNames[i] = records.get(i).getStr("name");
        }
        return columnNames;
    }
}
